/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
/* smbuilt.c ... some interactive bits - not needed in command line
   programs - in particular sm2c */
    
#define SM_CLASS extern
#include "smlib.h"
#include "smio.h"

CHAR *smbuilt_rcsid(void) {
    return("$Id: smbuilt.c,v 1.7 2008/02/19 13:15:50 bhepple Exp $");
}

#if !READ_DAT_AT_RUNTIME
/* Built-in screens & things */
static CHAR *sm_built_ins[] = {
#include "smbuilt.dat"
NULL
};
#endif

SM_FIELD  sm_prompt_fld = NULL,
	  sm_dumb_fld   = NULL;

void sm_read_built_ins()
{
    SM_FILE		screenset;

#if READ_DAT_AT_RUNTIME
    {
	CHAR	buf[MAXPATHLEN + 1];

	buf[0] = 0;
#if NEEDS_PATH_FOR_PROGS
	strcpy(buf, sm_program_directory);
#endif
	strcat(buf, "smbuilt.dat");
	if (sm_fopen(sm_builtin_screens(), buf) == SM_ERROR)
	    sm_error_exit("Error reading smbuilt.dat!");
    }
#else
    sm_dbopen(sm_builtin_screens(), sm_built_ins);
#endif
    screenset = sm_builtin_screens();

#include "smbuilt.opn"
    
    if ((sm_prompt_fld = sm_fld_open(sm_prompt_scr, "_prompt_field")) == NULL)
        sm_error_exit("Can't open field _prompt_field");

    if ((sm_dumb_fld = sm_fld_open(sm_dumb_scr, "_dumb_field")) == NULL)
        sm_error_exit("Can't open field _dumb_field");
    
    /* Reset read-file parameters to zero - will be set when reading file */
    sm_rd_num_fld_fields	= 0;
    sm_rd_num_bg_fields		= 0;
    sm_rd_num_scr_fields	= 0;
    sm_rd_num_skey_fields	= 0;
    sm_rd_num_menu_fields	= 0;
    sm_rd_num_file_fields	= 0;
    sm_rd_file_line_order	= NULL;
    sm_rd_fld_line_order	= NULL;
    sm_rd_bg_line_order		= NULL;
    sm_rd_scr_line_order	= NULL;
    sm_rd_menu_line_order	= NULL;
    sm_rd_skey_line_order	= NULL;
    
}

struct prompt_s {
    f_ptr	destructor;
    int		window;
    SM_SOFTKEY	skey_store;
};

int prompt_destructor ARG1(struct prompt_s *, object)
{
    free(object);
    return(0);
}

/* Can only return SM_YES_C, SM_CANCEL_C, SM_NO_SAVE_EXIT_C or SM_EXIT_C */
CALLBACK(sm_prompt_done)
{
    struct prompt_s 	*prompt = (struct prompt_s *) callback_data;
   
    if (command > SM_MAX_COMMAND) /* User function key */
	command = SM_QUIT_C;
    if ((command != SM_NEXT_LINE_C) &&
	(command != SM_QUIT_C) &&
	(command != SM_EXIT_C) &&
	(command != SM_NO_SAVE_EXIT_C) &&
	(command != SM_CANCEL_C)) { /* possible ? */
	sm_setup_scr(prompt->window, sm_prompt_scr,NULL, NULL, 0, -1, 0);
	return(0);
    }

#if SM_XAW
    if (smio_using_xaw)
	smxaw_prompt_done();
    else
#endif
	sm_del_win(prompt->window);

    if (command == SM_QUIT_C)
	command = SM_CANCEL_C;
    if (command == SM_NEXT_LINE_C)
	command =  SM_YES_C;
    sm_paint_skey(prompt->skey_store);
    return(command);
}

int sm_setup_prompt ARG4(CHAR *, 	prompt1, 
			 CHAR *, 	prompt2,
			 CHAR *, 	result_default,
			 SM_SOFTKEY, 	skey)
{
    struct prompt_s 	*prompt;
    SM_BACKGROUND	prompt1bg, prompt2bg;

    prompt = (struct prompt_s *)xmalloc((INDEX) sizeof(struct prompt_s));
    prompt->destructor = prompt_destructor;
    prompt->skey_store = smio_current_skey;
    if (prompt1 == NULL)
	prompt1 = "";
    if (prompt2 == NULL)
	prompt2 = "";

#if SM_XAW
    if (smio_using_xaw) {
	int	field_len = abs(sm_prompt_fld->len);

	SM_FASSIGN(sm_prompt_fld->c_value, result_default, field_len);
	smxaw_setup_prompt(prompt1, prompt2, sm_prompt_fld->c_value);
	prompt->window = 1;
    } else 
#endif
    {
	if ((prompt->window = sm_popup_win(5, 70, 8, 5)) < 0)
	    return(SM_ERROR);
	sm_fld_set_default(sm_prompt_fld, result_default);
	sm_scr_init(sm_prompt_scr, /* recursive = */ 1);
	
	prompt1bg = *sm_first_bg(sm_prompt_scr);
	prompt2bg = *sm_next_bg(sm_first_bg(sm_prompt_scr));
	prompt1bg->text = StrToString(prompt1bg->text, prompt1);
	truncString(prompt1bg->text, SM_MaxPromptLen + 1);
	prompt2bg->text = StrToString(prompt2bg->text, prompt2);
	truncString(prompt2bg->text, SM_MaxPromptLen + 1);
    }
	
    sm_add_callback(sm_prompt_done, prompt);
    sm_setup_scr(prompt->window, sm_prompt_scr, NULL, NULL, 0, 0, 0);
    if (skey)
	sm_paint_skey(skey);
    return(0);
}

CHAR	*sm_get_prompt_value()
{
    return(sm_prompt_fld->c_value);
}

/* Display a window of elements from the list 'data' and allow the user to
   scroll up and down and select one item by pressing 'RETURN'. The window
   is positioned at (row, col). The element number is returned in 'index'.
   Function 'f' is called after every keystroke to allow a helper routine
   do some of the work. 'f' can be NULL. 
*/

struct selection_s {
    f_ptr	destructor;
    SM_SCREEN	screen;
    INDEX	*index;
    int 	preferred_row;
    int 	preferred_col; 
    f_ptr 	helper;
    void	*helperdata;
    int	 	desired_cols;
    int	 	desired_rows;
    int		last_cols;
    int		last_rows;
    INDEX	first_displayed;
    INDEX	last_displayed;
    int	 	col;
    int		row;
    int		maxrow;
    int		maxcol;
    int		window_popped_up;
    int		window;
    int		scroll_by_one;
    SM_SOFTKEY	skey_store;
};

static int selection_destructor ARG1(struct selection_s *, object)
{
    assert(object);

    free(object);
    return(0);
}

static int prep_selection ARG1(struct selection_s *, selection)
{
    int		max_width, i;
    INDEX 	num_elements;
    CHAR	*element;

    assert(selection);

    selection->col = selection->preferred_col;
    selection->row = selection->preferred_row;
    if (selection->row > selection->maxrow - 4)
	selection->row = selection->maxrow - 4;

    /* Count the elements and find the widest one */
    smdb_seek(selection->screen, (INDEX) 0);
    max_width = 0;
    num_elements = 0;
    while ((element = smdb_get_current(selection->screen))) {
	if ((i = strlen(element)) > max_width)
	    max_width = i;
	num_elements++;
	smdb_read_next(selection->screen);
    }
    selection->screen->num_records = num_elements;
    smdb_seek(selection->screen, *(selection->index));
    
    if (max_width == 0)
	max_width = 20;
    
    (selection->desired_cols) = max_width;
    (selection->desired_rows) = num_elements;
    
    /* Adjust the size of the window to something suitable */
    if (max_width > selection->maxcol - selection->col)
	selection->col = selection->maxcol - max_width;
    if (selection->col < 0)
	selection->col = 0;
    if (selection->col + (selection->desired_cols) > selection->maxcol)
	(selection->desired_cols) = selection->maxcol - selection->col;
    
    if (num_elements > selection->maxrow - selection->row)
	selection->row = selection->maxrow - num_elements;
    if (selection->row < 0)
	selection->row = 0;
    if (selection->row + selection->desired_rows > selection->maxrow)
	(selection->desired_rows) = selection->maxrow - selection->row;
    
    /* See if we need to pop-up a new window (otherwise, the
       window flicks on and off after every keystroke!) */
    if ((selection->last_cols != selection->desired_cols) || 
	(selection->last_rows != selection->desired_rows)) {
	if (selection->window_popped_up)
	    sm_del_win(selection->window);
	if ((selection->window = 
	     sm_popup_win(selection->desired_rows + 2, 
			  selection->desired_cols + 2, 
			  selection->row, 
			  selection->col)) < 0)
	    return(SM_ERROR);
	selection->last_cols = selection->desired_cols;
	selection->last_rows = selection->desired_rows;
	selection->window_popped_up = 1;
	selection->first_displayed = *(selection->index) + 1000;
    }
    
    smio_clear(selection->window);
    
    if (smdb_get_current(selection->screen)) {
	if (smdb_enq_position(selection->screen) < selection->first_displayed) {
	    INDEX first = smdb_enq_position(selection->screen);
	    /* NB - INDEX may be unsigned therefore never < 0 */
	    
	    if (selection->scroll_by_one) {
		if (first)
		    first--;
	    } else {
		if (first >= (selection->desired_rows / 2))
		    first -= (selection->desired_rows / 2);
		else
		    first = 0;
	    }	
	    selection->first_displayed = first;
	}
	
	if (smdb_enq_position(selection->screen) >= 
	    selection->first_displayed + selection->desired_rows) {
	    INDEX first = selection->first_displayed;

	    if (selection->scroll_by_one)
		first++;
	    else
		first = smdb_enq_position(selection->screen) -
		        (selection->desired_rows / 2);

	    selection->first_displayed = first;
	}
	
	selection->last_displayed = selection->first_displayed;
	smdb_seek(selection->screen, selection->last_displayed);
	while (smdb_get_current(selection->screen) &&
	       (selection->last_displayed < 
		selection->first_displayed + 
		(selection->desired_rows))) {
	    smio_goto(selection->window, 
		      selection->last_displayed - 
		      selection->first_displayed, 
		      0);
	    smio_puts(selection->window, 
		      smdb_get_current(selection->screen));
	    selection->last_displayed++;
	    smdb_seek(selection->screen, selection->last_displayed);
	}
	if (selection->last_displayed >= 
	    selection->first_displayed + selection->desired_rows)
	    selection->last_displayed--;
    }
    
    /* Turn on cursor */
    if (smdb_seek(selection->screen, *(selection->index))) {
	smio_enh(selection->window, SM_STANDOUT);
	smio_goto(selection->window, 
		  *(selection->index) - selection->first_displayed, 
		  0);
	smio_puts(selection->window, 
		  smdb_get_current(selection->screen));
    }
    return(0);
}

CALLBACK(sm_do_selection)
{
    struct selection_s 	*selection = (struct selection_s *) callback_data;
    int	 		num_elements;
    
    assert(selection);

#ifdef SM_XAW
    if (smio_using_xaw) {
	/* helper is always NULL for xaw - the only other users are
	   smcreate (which is not to use xaw) and getfl which has its own
	   xaw file selector */
	smxaw_do_selection(selection->screen, selection->index);
	switch (command) {
	case SM_NEXT_FIELD_C:
	case SM_NEXT_LINE_C:
	    command = SM_YES_C;
	case SM_QUIT_C:
	default:
	    if (selection->skey_store)
		sm_paint_skey(selection->skey_store);
	    return(command);
	} 
    } else /* curses ... */
#endif
    {
	selection->scroll_by_one = 0;

	/* Turn off cursor */
	if (smdb_get_current(selection->screen)) {
	    smio_enh(selection->window, SM_NORMAL);
	    smio_goto(selection->window, 
		      *(selection->index) - selection->first_displayed, 
		      0);
	    smio_puts(selection->window, smdb_get_current(selection->screen));
	}
	
	/* Helper function */
	if ((selection->helper) && (command > 0)) {
	    int i = *selection->index - selection->first_displayed;
	    
	    command = (*(selection->helper))
		(command,
		 &(selection->screen->data),
		 selection->index,
		 /* for the border */
		 i,
		 selection->col + selection->desired_cols,
		 selection->helperdata);
	}
	
	/* Count the elements again */
	num_elements = smdb_num_records(selection->screen);
        selection->screen->num_records = num_elements; /* BH 20111218 */
	smdb_seek(selection->screen, *selection->index);
	
	switch (command) {
	    
	case SM_UP_C:
	    smdb_read_prev(selection->screen);
	    break;
	    
	case SM_DOWN_C: 
	    if (smdb_enq_position(selection->screen) <
		smdb_num_records(selection->screen) - 1)
		smdb_read_next(selection->screen); 
	    break;
	    
	case SM_HOME_FIELD_C:
	case SM_HOME_SCREEN_C:
	case SM_FIRST_SCREEN_C:
	    smdb_seek(selection->screen, (INDEX) 0);
	    break;
	    
	case SM_END_FIELD_C:
	case SM_END_SCREEN_C:
	case SM_LAST_SCREEN_C:
	    smdb_seek(selection->screen, MAX_INDEX);
	    smdb_read_prev(selection->screen);
	    break;
	    
	case SM_NEXT_SCREEN_C:	
	    if (smdb_enq_position(selection->screen) == selection->last_displayed){
		if (smdb_seek(selection->screen,
			      smdb_enq_position(selection->screen) + 1 +
			      (selection->desired_rows - 1)/ 2) == NULL) {
		    smdb_seek(selection->screen, MAX_INDEX);
		    smdb_read_prev(selection->screen);
		}
	    } else
		smdb_seek(selection->screen, selection->last_displayed);
	    if (smdb_get_current(selection->screen) == NULL)
		smdb_read_prev(selection->screen);
	    break;
	    
	case SM_PREV_SCREEN_C:
	    if (smdb_enq_position(selection->screen) == 
		selection->first_displayed) {
		if (smdb_seek(selection->screen,
			      smdb_enq_position(selection->screen) + 1 -
			      (selection->desired_rows + 1)/ 2) == NULL) {
		    smdb_seek(selection->screen, (INDEX) 0);
		}
	    } else
		smdb_seek(selection->screen, selection->first_displayed);
	    if (smdb_get_current(selection->screen) == NULL)
		smdb_read_next(selection->screen);
	    break;
	    
	case SM_MOUSE_EVENT_C:
	    {
		int	mouse_button, mouse_row, mouse_col, mouse_delta;
		INDEX	new_pos;
		
		mouse_button = sm_enq_mouse(&mouse_row, 
					    &mouse_col, 
					    &mouse_delta);
		selection->scroll_by_one = 1;
		new_pos = selection->first_displayed + mouse_row;
		if (new_pos < 0)
		    new_pos = 0;
		if (new_pos >= smdb_num_records(selection->screen))
		    new_pos = smdb_num_records(selection->screen) - 1;
		smdb_seek(selection->screen, new_pos);
		*(selection->index) = smdb_enq_position(selection->screen);
		if ((mouse_button & SM_MOUSE_UP) &&
		    (mouse_button & SM_MOUSE_LEFT)) {
		    sm_del_win(selection->window);
		    if (selection->skey_store)
			sm_paint_skey(selection->skey_store);
		    return(SM_YES_C);
		}
	    }
	    break;
	    
	case SM_POPUP_C: /* Possibly pushed by mouse-right-button-down */
	case 0: /* helper has probably consumed the command */
	    break;
	    
	case SM_NEXT_FIELD_C:
	case SM_NEXT_LINE_C:
	    command = SM_YES_C;
	case SM_QUIT_C:
	default:
	    sm_del_win(selection->window);
	    *(selection->index) = smdb_enq_position(selection->screen);
	    if (selection->skey_store)
		sm_paint_skey(selection->skey_store);
	    return(command);
	}
	
	*(selection->index) = smdb_enq_position(selection->screen);
	prep_selection(selection);
	return(0);
    }
}

void sm_setup_selection ARG7(SM_SCREEN,	screen,
			     INDEX *,	index,
			     int, 	preferred_row, 
			     int, 	preferred_col, 
			     f_ptr, 	helper,
			     void *,	helperdata,
			     SM_SOFTKEY,skey)
{
    struct selection_s *selection;
    
    selection = (struct selection_s *)xmalloc((INDEX) sizeof(struct selection_s));
    selection->destructor 	= selection_destructor;
    selection->screen 		= screen;
    selection->index		= index;
    selection->preferred_row 	= preferred_row;
    selection->preferred_col 	= preferred_col;
    selection->helper		= helper;
    selection->helperdata	= helperdata;
    selection->last_cols 	= -1;
    selection->last_rows 	= -1,
    selection->first_displayed 	= *(selection->index) + 2;
    /* ... to provoke a redisplay */
    selection->col 		= selection->preferred_col;
    selection->row 		= selection->preferred_row;
    smio_enq_screen(&selection->maxrow, &selection->maxcol);
    selection->maxrow 		-= 2; /* for the borders */;
    selection->maxcol 		-= 2;
    selection->window_popped_up = 0;
    selection->scroll_by_one	= 0;
    if (skey) {
	selection->skey_store = NULL;
	sm_paint_skey(skey);
    } else {
	selection->skey_store = smio_current_skey;
	sm_paint_skey(sm_selection_skey);
    }
    sm_add_callback(sm_do_selection, selection);
    
#ifdef SM_XAW
    if (smio_using_xaw) {
	/* helper is always NULL for xaw - the only other users are
	   smcreate (which is not to use xaw) and getfl which has its own
	   xaw file selector */
	smxaw_setup_selection(screen, index, skey);
    } else
#endif
	prep_selection(selection);
}

int sm_confirm ARG1(CHAR *, msg)
{
    SM_SOFTKEY	skey_store;
    int		retval;

    skey_store = smio_current_skey;
    sm_paint_skey(sm_confirm_skey);
    retval = smio_confirm(msg);
    sm_paint_skey(skey_store);
    return(retval);
}

/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
